#pragma once

#include <any>
#include <cstdint>
#include <map>
#include <set>
#include <string>

#include <refbase.h>

class SingletonContainer : public RefBase {
public:
    static const sptr<SingletonContainer> &GetInstance();

    void AddSingleton(const std::string &name, const std::any &instance);
    void SetSingleton(const std::string &name, const std::any &instance);
    const std::any& GetSingleton(const std::string &name);
    const std::any& DependOn(const std::string &instance, const std::string &name);

    template<class T>
    static sptr<T> Get()
    {
        std::string nameT = __PRETTY_FUNCTION__;
        nameT = nameT.substr(nameT.find("T = "));
        nameT = nameT.substr(sizeof("T ="), nameT.length() - sizeof("T = "));

        using sptrT = sptr<T>;
        sptrT ret = nullptr;
        const std::any &instance = SingletonContainer::GetInstance()->GetSingleton(nameT);
        auto pRet = std::any_cast<sptr<T>>(&instance);
        if (pRet != nullptr) {
            ret = *pRet;
        }
        return ret;
    }

    template<class T>
    static void Set(const sptr<T> &ptr)
    {
        std::string nameT = __PRETTY_FUNCTION__;
        nameT = nameT.substr(nameT.find("T = "));
        nameT = nameT.substr(sizeof("T ="), nameT.length() - sizeof("T = "));

        SingletonContainer::GetInstance()->SetSingleton(nameT, ptr);
    }

private:
    SingletonContainer() = default;
    virtual ~SingletonContainer() override;
    static inline sptr<SingletonContainer> instance = nullptr;

    struct Singleton {
        std::any value;
        int32_t refCount;
    };
    std::map<std::string, int32_t> stringMap;
    std::map<int32_t, SingletonContainer::Singleton> singletonMap;
    std::map<int32_t, std::set<int32_t>> dependencySetMap;
};
